#include "proxy/user/userlogin.h"

namespace proxy{

  UserLoginSession::UserLoginSession(boost::asio::io_service& io_service, int timeout)
    :io_service_(io_service),
    timeout_(timeout),
    ping_times_(0){

  }

  UserLoginSession::~UserLoginSession(){
    LOG(WARNING) << "Delte UserLoginSession";
  }

  void UserLoginSession::Start(const std::string user_name,
    const std::string password,
    const std::string server_addr,
    unsigned short server_port){

    connect_ = vzconn::TcpClient::ConnectServer(
      io_service_,
      server_addr,
      server_port,
      boost::bind(&UserLoginSession::OnServerConnect, shared_from_this(), 
      _1, _2, user_name, password));
  }

  void UserLoginSession::Stop(){
    if (connect_){
      connect_->CloseConnect();
    }
  }

  void UserLoginSession::StartPingMessage(){
    timer_.reset(new boost::asio::deadline_timer(
      io_service_, boost::posix_time::seconds(timeout_)));
    timer_->async_wait(
      boost::bind(&UserLoginSession::HandleTimeout, shared_from_this(),
      boost::asio::placeholders::error));
  }

  void UserLoginSession::OnServerConnect(vzconn::TcpConnect::Ptr connect,
    const boost::system::error_code& err, const std::string user_name,
    const std::string password){
    BOOST_ASSERT(connect == connect_);
    if (err){
      LOG(ERROR) << err.message();
      SignalLoginFailure(shared_from_this(), err);
      return;
    }

    connect_->SignalConnectError.connect(
      boost::bind(&UserLoginSession::OnConnectError, shared_from_this(), _1, _2));
    connect_->SignalConnectWrite.connect(
      boost::bind(&UserLoginSession::OnConnectWrite, shared_from_this(), _1));
    connect_->SignalConnectRead.connect(
      boost::bind(&UserLoginSession::OnConnectRead, shared_from_this(), _1, _2, _3, _4));


    StartLogin(user_name, password);
  }

  void UserLoginSession::StartLogin(
    const std::string user_name, const std::string password){

    boost::shared_ptr<vzconn::ByteBuffer> bytebuffer(new vzconn::ByteBuffer());
    // node type
    bytebuffer->WriteUInt8(NODE_TYPE_CLIENT);
    // user name size
    bytebuffer->WriteUInt8(user_name.size());
    // user name
    bytebuffer->WriteBytes(user_name.c_str(), user_name.size());
    // password size
    bytebuffer->WriteUInt8(password.size());
    // password
    bytebuffer->WriteBytes(password.c_str(), password.size());

    if (connect_){
      connect_->AsyncWrite(bytebuffer, FLAG_REGISTER_PING);
    }
    else{
      LOG(ERROR) << "The connect disable";
      SignalLoginFailure(shared_from_this(), boost::asio::error::invalid_argument);
    }

  }

  bool UserLoginSession::StartGetOnlineProxyServer(const char* user_name, int size,
    SignalOnlineServerListCallback callback){
    SignalOnlineServerList = callback;
    if (connect_){
      connect_->AsyncWrite(user_name, size, FLAG_PROXY_ONLINE_SERVER_LIST);
      return true;
    }
    return false;
  }

  void UserLoginSession::HandleTimeout(const boost::system::error_code& err){
    if (!connect_){
      LOG(ERROR) << "connect_ delete";
      return;
    }
    if (err){
      LOG(ERROR) << err.message();
      SignalLoginFailure(shared_from_this(), err);
      return;
    }
    if (ping_times_ <= PING_TIMEOUT_TIMES){
      ping_times_++;
      int64 current_time = 0;
      time(&current_time);
      connect_->AsyncWrite((const char *)&current_time, 8, FLAG_PROXY_SERVER_PING_MESSAGE);

      timer_->expires_from_now(boost::posix_time::seconds(timeout_));
      timer_->async_wait(
        boost::bind(&UserLoginSession::HandleTimeout, shared_from_this(),
        boost::asio::placeholders::error));
    }
    else{
      LOG(ERROR) << "Ping time out";
      SignalLoginFailure(shared_from_this(), err);
    }
  }


  void UserLoginSession::OnConnectError(vzconn::TcpConnect::Ptr connect,
    const boost::system::error_code& err){
    connect_.reset();
    SignalLoginFailure(shared_from_this(), err);
  }

  void UserLoginSession::OnConnectWrite(vzconn::TcpConnect::Ptr connect){
    // Donothing
  }

  void UserLoginSession::OnConnectRead(vzconn::TcpConnect::Ptr connect,
    const char* buffer, int size, int flag){
    if (flag == FLAG_PROXY_SERVER_PING_MESSAGE){
      LOG(INFO) << "FLAG_PROXY_SERVER_PING_MESSAGE ";
      connect_->AsyncWrite(buffer, size, FLAG_PROXY_SERVER_PING_REPLY);
    }
    else if (flag == FLAG_PROXY_SERVER_PING_REPLY){
      ping_times_ = 0;
      uint64 ping_time = *(uint64*)(buffer);
      int64 current_time = 0;
      time(&current_time);
      LOG(INFO) << "PING " << current_time - ping_time;
    }
    else if (flag == FLAG_PROXY_ONLINE_SERVER_LIST){
      SignalOnlineServerList(shared_from_this(), buffer, size);
    }
    else if (flag == FLAG_USER_LOGIN_SUCCEED){
      LOG(INFO) << "Register succeed";
      StartPingMessage();
      SignalLoginSucceed(shared_from_this());
    }
    else if (flag == FLAG_USER_LOGIN_FAILURE){
      LOG(INFO) << "Register error";
      SignalLoginFailure(shared_from_this(), boost::asio::error::invalid_argument);
    }
    else{
      LOG(ERROR) << "Nothing to do this";
    }
  }
}